package cn.xinfei.xdecision.common.service.datax.datamanage.v2.impl;

import cn.xinfei.xdecision.common.dao.datax.datamanage.v2.*;
import cn.xinfei.xdecision.common.model.common.ResponseEntityDto;
import cn.xinfei.xdecision.common.model.common.VarPageResult;
import cn.xinfei.xdecision.common.model.common.requestParam.UpdateFolderParam;
import cn.xinfei.xdecision.common.model.datax.common.ExcelUtil;
import cn.xinfei.xdecision.common.model.datax.consts.VariableConst;
import cn.xinfei.xdecision.common.model.datax.datainterface.v2.VarInterfaceInfo;
import cn.xinfei.xdecision.common.model.datax.datainterface.v2.VariableVO;
import cn.xinfei.xdecision.common.model.datax.datamanage.request.*;
import cn.xinfei.xdecision.common.model.datax.datamanage.v2.*;
import cn.xinfei.xdecision.common.model.datax.datamanage.v2.resp.VariableResp;
import cn.xinfei.xdecision.common.model.datax.datamanage.v2.vo.*;
import cn.xinfei.xdecision.common.model.datax.enums.EnumUtil;
import cn.xinfei.xdecision.common.model.datax.enums.ICodeEnum;
import cn.xinfei.xdecision.common.model.datax.enums.VariableEnum;
import cn.xinfei.xdecision.common.model.datax.exception.XDecisionDataException;
import cn.xinfei.xdecision.common.model.datax.exception.XDecisionDataExceptionType;
import cn.xinfei.xdecision.common.model.login.SessionManager;
import cn.xinfei.xdecision.common.service.common.DataConvertUtil;
import cn.xinfei.xdecision.common.service.datax.datainterface.v2.VarInterfaceInfoService;
import cn.xinfei.xdecision.common.service.datax.datainterface.v2.VariableInterfaceService;
import cn.xinfei.xdecision.common.service.datax.datamanage.v2.VariableParamService;
import cn.xinfei.xdecision.common.service.datax.datamanage.v2.VariableScriptService;
import cn.xinfei.xdecision.common.service.datax.datamanage.v2.VariableService;
import cn.xinfei.xdecision.common.service.datax.datamanage.v2.VariableSnapshotService;
import cn.xinfei.xdecision.common.utils.exception.ApiException;
import cn.xinfei.xdecision.common.utils.exception.enums.ErrorCodeEnum;
import cn.xinfei.xdecision.common.utils.util.BeanUtilsPlus;
import cn.xinfei.xdecision.common.utils.util.DateUtils;
import cn.xinfei.xdecision.common.utils.util.FutureUtils;
import cn.xinfei.xdecision.common.utils.util.data.VariableNumericUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collector;
import java.util.stream.Collectors;

@Slf4j
@Service
public class VariableServiceImpl extends ServiceImpl<VariableMapper, Variable> implements VariableService {


    @Resource
    VariableMapper variableMapper;

    @Autowired
    VarInterfaceInfoService varInterfaceInfoService;

    @Autowired
    VariableInterfaceService variableInterfaceService;

    @Resource
    VariableUserMapper variableUserMapper;

    @Resource
    VariableTypeMapper variableTypeMapper;

    @Resource
    VariableTypeUserMapper variableTypeUserMapper;

    @Autowired
    VariableScriptService variableScriptService;

    @Autowired
    VariableParamService variableParamService;

    @Resource
    VariableDetailMapper variableDetailMapper;

    @Resource
    VariableScriptMapper variableScriptMapper;

    @Resource
    VariableParamMapper variableParamMapper;

    @Resource
    VariableSnapshotMapper variableSnapshotMapper;

    @Resource
    VariableSnapshotService variableSnapshotService;
    @Resource
    OrganUserMapper organUserMapper;

    @Autowired
    TransactionTemplate transactionTemplate;

    @Resource(name = "bizProcessorThreadPool")
    private ExecutorService bizProcessorThreadPool;


    @Override
    public List<Long> findDistinctInterfaceIdsByVarCodes(Collection<String> varCodes) {
        if (CollectionUtils.isEmpty(varCodes)) {
            return Collections.emptyList();
        }
        return variableMapper.findDistinctInterfaceIdsByVarCodes(varCodes);
    }

    @Override
    public Map<Integer, Set<String>> findVarTypeAndVarCodesMap(Collection<String> varCodes) {
        if (CollectionUtils.isEmpty(varCodes)) {
            return Collections.emptyMap();
        }
        return varCodes.stream()
//                .collect(Collectors.groupingBy(key ->  variableMapper.findExecutorVariableTypeByVarCode(key)));
                .collect(Collectors.groupingBy(key -> variableMapper.findExecutorVariableTypeByVarCode(key), Collectors.toSet()));
    }

    @Override
    public Map<Integer, Set<String>> findVarTypeAndVarCodesMapFromCache(Collection<String> varCodes, Map<String, Variable> variableCache) {

        if (CollectionUtils.isEmpty(varCodes)) {
            return Collections.emptyMap();
        }

        return varCodes.stream()
                .collect(Collectors.groupingBy(key ->
                                variableCache.values().stream()
                                        .filter(var -> key.equals(var.getVarCode()))
                                        .map(Variable::getVarType)
                                        .filter(Objects::nonNull)
                                        .findFirst().get(),
                        Collectors.toSet()));


    }

    @Override
    public Boolean checkVirtualDataSource(String varCode) {
        List<Long> interfaceIds = variableMapper.findDistinctInterfaceIdsByVarCodes(Collections.singleton(varCode));
        if (CollectionUtils.isEmpty(interfaceIds)) {
            return Boolean.FALSE;
        }
        return variableMapper.findExecutorVariableTypeByVarCode(varCode).equals(VariableConst.INTERFACE_TYPE)
                && CollectionUtils.isNotEmpty(varInterfaceInfoService.selectVirtualDataSourceIdsByIds(interfaceIds));
    }

    @Override
    public List<Variable> findVariables(Collection<String> varCodes) {
        if (CollectionUtils.isEmpty(varCodes)) {
            return Collections.emptyList();
        }
        return variableMapper.findByVarCodes(varCodes);
    }

    @Override
    public List<Variable> findVariablesByOrganId(Collection<String> varCodes, Long organId) {
        if (CollectionUtils.isEmpty(varCodes)) {
            return Collections.emptyList();
        }
        return variableMapper.findByVarCodesAndOrganId(varCodes, organId);
    }

    @Override
    public List<Variable> findBussinessVarsByOrganId(Long organId) {

        List<String> deriveVars = VariableConst.BUSSINESS_LABEL_DERIVE_VARS;
        List<String> varCodes = variableInterfaceService.selectVarCodesByProviderAndOrganId(
                VariableConst.BUSSINESS_PROVIDER,
                organId);
        varCodes.addAll(deriveVars);
        return findVariablesByOrganId(varCodes, organId);
    }

    @Override
    public List<Variable> findVariables() {
        return variableMapper.findAllVars();
    }

    @Override
    public List<String> findAllVarCodes() {
        List<Variable> variables = new LambdaQueryChainWrapper<>(variableMapper)
                .select(Variable::getVarCode)
                .eq(Variable::getIsDeleted, "N")
                .list();
        return Optional.ofNullable(variables)
                .orElseGet(Collections::emptyList)
                .stream()
                .map(Variable::getVarCode)
                .collect(Collectors.toList());
    }

    @Override
    public List<String> findAllDeriveVars() {
        List<Variable> variables = new LambdaQueryChainWrapper<>(variableMapper)
                .select(Variable::getVarCode)
                .eq(Variable::getIsDeleted, "N")
                .eq(Variable::getVarType, 4)
                .list();
        return Optional.ofNullable(variables)
                .orElseGet(Collections::emptyList)
                .stream()
                .map(Variable::getVarCode)
                .collect(Collectors.toList());
    }


    @Override
    public Variable findByVarCodeAndOrganId(String varCode, Long organId) {
        return variableMapper.findByVarCodeAndOrganId(varCode, organId);
    }

    @Override
    public List<Variable> findByVarCodesAndOrganId(Collection<String> varCodes, Long organId) {
        return variableMapper.findByVarCodesAndOrganId(varCodes, organId);
    }

    @Override
    public Variable findVarTypeByVarCodes(String varCode) {
        return variableMapper.findByVarCode(varCode);
    }

    @Override
    public Variable findByVarName(String varName) {
        return variableMapper.findByVarName(varName);
    }

    @Override
    public Set<String> findVarCodesByInterfaceId(Long interfaceId, Long organId) {
        List<Variable> varCodeList = new LambdaQueryChainWrapper<>(variableMapper)
                .select(Variable::getVarCode)
                .eq(Objects.nonNull(interfaceId), Variable::getInterfaceId, interfaceId)
                .eq(Objects.nonNull(organId), Variable::getOrganId, organId)
                .list();
        return Optional.ofNullable(varCodeList)
                .orElseGet(Collections::emptyList)
                .stream()
                .map(Variable::getVarCode)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
    }

    @Override
    public List<String> findScriptVarCodes(Collection<String> varCodes) {
        if (CollectionUtils.isEmpty(varCodes)) {
            return Collections.emptyList();
        }
        return variableMapper.findScriptVarCodesByVarCodes(varCodes);
    }

    @Override
    public Boolean checkVarIsDerive(String varCode) {
        Variable byVarCode = variableMapper.findByVarCode(varCode);
        if (null != byVarCode) {
            return byVarCode.getVarType().equals(VariableConst.DERIVE_VAR_TYPE);
        }
        return false;
    }

    @Override
    public Boolean checkVarCodeIsDuplicate(String varCode, Long organId) {
        Variable var = new LambdaQueryChainWrapper<>(variableMapper)
                .select(Variable::getVarCode)
                .eq(StringUtils.isNotEmpty(varCode), Variable::getVarCode, varCode)
                .eq(Objects.nonNull(organId), Variable::getOrganId, organId)
                .one();
        return StringUtils.isNotEmpty(
                Optional.ofNullable(var).map(Variable::getVarCode).orElse(null)
        ) ? Boolean.TRUE : Boolean.FALSE;
    }

    @Override
    public Boolean checkVarNameIsDuplicate(String varName, Long organId) {
        AtomicReference<Boolean> varNameDuiplicated = new AtomicReference<>(false);
        List<Variable> vars = new LambdaQueryChainWrapper<>(variableMapper)
                .select(Variable::getVarName)
                .eq(StringUtils.isNotEmpty(varName), Variable::getVarName, varName)
                .eq(Objects.nonNull(organId), Variable::getOrganId, organId)
                .list();

        Optional.ofNullable(vars)
                .orElse(Collections.emptyList())
                .stream()
                .findFirst()
                .ifPresent(item -> {
                    if (StringUtils.isNotBlank(item.getVarName())) {
                        varNameDuiplicated.set(true);
                    }
                });
        return varNameDuiplicated.get();
    }

    @Override
    public Boolean checkVarNameIsDuplicateOnUpdate(String varCode, String varName, Long organId) {
        String updateVarName = Optional.ofNullable(findByVarCodesAndOrganId(Collections.singletonList(varCode), organId))
                .map(item -> item.get(0)).map(Variable::getVarName).orElse(null);
        Variable var = new LambdaQueryChainWrapper<>(variableMapper)
                .select(Variable::getVarName)
                .eq(StringUtils.isNotEmpty(varName), Variable::getVarName, varName)
                .ne(StringUtils.isNotEmpty(updateVarName), Variable::getVarName, updateVarName)
                .eq(Objects.nonNull(organId), Variable::getOrganId, organId)
                .one();
        return StringUtils.isNotEmpty(
                Optional.ofNullable(var).map(Variable::getVarName).orElse(null)
        ) ? Boolean.TRUE : Boolean.FALSE;
    }

    @Override
    public Map<String, Integer> findVarCodeAndVarTypeMap(Collection<String> varCodes) {

        if (CollectionUtils.isEmpty(varCodes)) {
            return Collections.emptyMap();
        }
        return variableMapper.findByVarCodes(varCodes)
                .stream()
                .collect(HashMap::new, (map, entry) -> map.put(entry.getVarCode(), entry.getVarType()), HashMap::putAll);

    }


    @Override
    public List<Variable> searchVariable(Map<String, Object> paramMap) {
        return variableMapper.findAllVariables(paramMap);
    }

    @Override
    public List<Variable> findAllDefaultVars(Map<String, Object> paramMap) {
        return variableMapper.searchAllVariable(paramMap);
    }

    @Override
    public boolean checkDefaultSearchVars(Map<String, Object> paramMap) {

        return CollectionUtils.isEmpty((List) paramMap.get("varCodes")) &&
                CollectionUtils.isEmpty((List) paramMap.get("varTypes")) &&
                (int) paramMap.getOrDefault("isInput", 0) == 0 &&
                (int) paramMap.getOrDefault("isOutput", 0) == 0 &&
                (int) paramMap.getOrDefault("isInner", 0) == 0 &&
                StringUtils.isBlank((String) paramMap.get("searchKey"));
    }

    public Collection<Variable> buildListSearchVars(Map<String, Object> paramMap, List<Variable> allVariables) {
        String search = (String) paramMap.get("searchKey");
        if (StringUtils.isEmpty(search)) {
            return allVariables;
        }
        HashSet<Variable> variables = new HashSet<>();
        allVariables.forEach(variable -> {
            String variableStr = variable.getVarCode() + variable.getVarName();
            if (variableStr.contains(search)) {
                variables.add(variable);
            }
        });
        return variables;
    }

    @Override
    public List<VariableResp> searchNoDerivedVariable(Map<String, Object> paramsMap) {
        List<VariableResp> variableRespList = Lists.newArrayList();
        List<Variable> allNotDeriveVariables = variableMapper.findAllNotDeriveVariables(paramsMap);
        if (CollectionUtils.isNotEmpty(allNotDeriveVariables)) {
            variableRespList = allNotDeriveVariables.stream().map(DataConvertUtil::convertToVarResp)
                    .collect(Collectors.toList());
        }
        return variableRespList;
    }

    @Override
    public List<Variable> searchNoDerivedVars(Map<String, Object> paramsMap) {
        return variableMapper.findAllNotDeriveVariables(paramsMap);
    }


    @Override
    public List<VariableDTO> findAllByVarCodes(Collection<String> varCodes) {
        List<Variable> variableList = variableMapper.findByVarCodes(varCodes);
        List<VariableDTO> variableDTOS = new ArrayList<>();

        variableList.forEach(var -> {
            VariableDTO variableDTO;
            VariableEnum variableEnum = EnumUtil.fromCode(VariableEnum.class, String.valueOf(var.getVarType()));
            String script = null;
            if (variableEnum.equals(VariableEnum.DERIVE_VAR)) {
                script = variableScriptService.findScriptByVariableCode(var.getVarCode());
                if (StringUtils.isEmpty(script)) {
                    throw new XDecisionDataException(
                            XDecisionDataExceptionType.DERIVE_PARAM_SCRIPT_ERROR,
                            "脚本不存在"
                    );
                }
            }
            Set<String> varParamCodes = variableParamService.getVarParamCode(var.getVarCode());
            variableDTO = DataConvertUtil.convertToVarDTO(var, script, varParamCodes);
            variableDTOS.add(variableDTO);
        });

        return variableDTOS;
    }

    @Override
    public Collection<VariableVO> findVarTypeByVarCodes(Collection<String> varCodes) {
        if (CollectionUtils.isEmpty(varCodes)) {
            return Collections.emptyList();
        }
        List<VariableVO> creditVarByVarCodes = variableMapper.findCreditVarByVarCodes(varCodes);
        return Optional.of(creditVarByVarCodes)
                .orElseGet(Collections::emptyList)
                .stream()
                .filter(var -> VariableConst.ZHENG_XIN.equals(var.getProvider()))
                .collect(Collectors.toList());
    }

    @Override
    public List<VariableVO> fineVarVosByVarCodes(Collection<String> varCodes) {
        if (CollectionUtils.isEmpty(varCodes)) {
            return Collections.emptyList();
        }

        return variableMapper.findVarVosByVarCodes(varCodes);
    }

    @Override
    public List<String> queryByFolderId(Long folderId) {
        QueryWrapper<Variable> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("folder_id", folderId);
        queryWrapper.select("var_code");

        List<Object> varCodes = this.listObjs(queryWrapper);
        if (varCodes == null) {
            return null;
        }
        return varCodes.stream().map(String::valueOf).collect(Collectors.toList());

    }

    @Override
    public List<VariableDepVO> findVarDeps(String varCode, Long organId) {
        Variable var = variableMapper.findByVarCodeAndOrganId(varCode, organId);
        List<VariableDepVO> depVarList = new ArrayList<>();
        if (Objects.isNull(var)) {
            return depVarList;
        }
        Set<String> varParamCodes;
        List<String> interfaceParamCodes = new ArrayList<>();
        VariableEnum variableEnum = EnumUtil.fromCodeOrThrow(VariableEnum.class, String.valueOf(var.getVarType()));
        if (VariableEnum.INTERFACE_VAR.equals(variableEnum)) {
            interfaceParamCodes = Optional
                    .ofNullable(varInterfaceInfoService.selectInterfaceById(var.getInterfaceId()))
                    .map(VarInterfaceInfo::getInputParam)
                    .map(item -> StringUtils.split(item, ","))
                    .map(Arrays::asList)
                    .orElse(Collections.emptyList());
        }

        varParamCodes = variableParamService.getVarParamCode(varCode);
        varParamCodes.addAll(new HashSet<>(interfaceParamCodes));

        if (CollectionUtils.isNotEmpty(varParamCodes)) {
            depVarList = variableMapper.findByVarCodesAndOrganId(varParamCodes, organId)
                    .parallelStream()
                    .filter(Objects::nonNull)
                    .map(item -> {
                        VariableDepVO variableDepVO = new VariableDepVO();
                        variableDepVO.setVarCode(item.getVarCode());
                        variableDepVO.setDefaultValue(item.getVarDefaultValue());
                        variableDepVO.setVarName(item.getVarName());
                        variableDepVO.setVarType(String.valueOf(item.getVarType()));
                        variableDepVO.setValueType(String.valueOf(item.getVarValueType()));
                        return variableDepVO;
                    }).collect(Collectors.toList());
        }
        return depVarList;
    }

    @Override
    public List<VariableDepVO> findVarsDep(List<String> varCodes, Long organId) {

        List<Variable> variableList = variableMapper.findByVarCodesAndUserId(varCodes, organId);
        List<VariableDepVO> depVarList = new ArrayList<>();
        if (CollectionUtils.isEmpty(variableList)) {
            return depVarList;
        }
        Set<String> varParamCodes;
        Set<String> interfaceParamCodes;
        interfaceParamCodes = variableList.parallelStream()
                .filter(var -> VariableEnum.INTERFACE_VAR.equals(
                        EnumUtil.fromCodeOrThrow(VariableEnum.class, String.valueOf(var.getVarType())))
                )
                .map(var ->
                        Optional
                                .ofNullable(varInterfaceInfoService.selectInterfaceById(var.getInterfaceId()))
                                .map(VarInterfaceInfo::getInputParam)
                                .map(item -> StringUtils.split(item, ","))
                                .map(Arrays::asList)
                                .orElse(Collections.emptyList())
                ).flatMap(Collection::stream)
                .collect(Collectors.toSet());

        varParamCodes = variableList.parallelStream()
                .filter(var -> VariableEnum.DERIVE_VAR.equals(
                        EnumUtil.fromCodeOrThrow(VariableEnum.class, String.valueOf(var.getVarType())))
                )
                .map(var -> variableParamService.getVarParamCode(var.getVarCode()))
                .flatMap(Collection::stream)
                .collect(Collectors.toSet());

        varParamCodes.addAll(interfaceParamCodes);
        if (CollectionUtils.isNotEmpty(varParamCodes)) {
            depVarList = variableMapper.findByVarCodesAndOrganId(varParamCodes, organId)
                    .parallelStream()
                    .filter(Objects::nonNull)
                    .map(item -> {
                        VariableDepVO variableDepVO = new VariableDepVO();
                        variableDepVO.setVarCode(item.getVarCode());
                        variableDepVO.setDefaultValue(item.getVarDefaultValue());
                        variableDepVO.setVarName(item.getVarName());
                        variableDepVO.setVarType(String.valueOf(item.getVarType()));
                        variableDepVO.setValueType(String.valueOf(item.getVarValueType()));
                        variableDepVO.setIsInput(item.getIsInput());
                        variableDepVO.setIsInner(item.getIsInner());
                        variableDepVO.setIsOutput(item.getIsOutput());
                        variableDepVO.setUpdateTime(item.getUpdateTime());
                        return variableDepVO;
                    }).collect(Collectors.toList());
        }
        return depVarList;
    }

    @Override
    public Set<String> getVarDepVars(long organId, Variable var) {
        return findVarDeps(var.getVarCode(), organId).stream()
                .map(VariableDepVO::getVarCode).filter(Objects::nonNull).collect(Collectors.toSet());
    }

    @Override
    public Map<String, List<VariableDepVO>> getVarsDep(long organId, List<Variable> vars) {
        if (CollectionUtils.isEmpty(vars)) {
            log.info("varCodes is empty");
            return Maps.newHashMap();
        }
        List<Future<Void>> allFutureList = Lists.newArrayList();
        Map<String, List<VariableDepVO>> varAndDepVarsMap = new HashMap<>();
        vars.forEach(var -> allFutureList.add(CompletableFuture.runAsync(() -> {
            String varCode = var.getVarCode();
            List<VariableDepVO> depVars = findVarDeps(varCode, organId);
            varAndDepVarsMap.put(varCode, depVars);
        }, bizProcessorThreadPool)));

        allFutureList.forEach(x -> FutureUtils.futureGet(x, ErrorCodeEnum.VAR_DEP_SEARCH_ERROR.getCode(),
                ErrorCodeEnum.VAR_DEP_SEARCH_ERROR.getMessage()));
        return varAndDepVarsMap;
    }

    @Override
    public List<VariableDepVO> findDepVars(String varCode, Long organId) {
        Variable var = variableMapper.findByVarCodeAndOrganId(varCode, organId);
        List<VariableDepVO> depVarList = new ArrayList<>();
        if (Objects.isNull(var)) {
            return depVarList;
        }
        Set<String> varParamCodes;
        List<String> interfaceDepParamCodes = new ArrayList<>();
        VariableEnum variableEnum = EnumUtil.fromCodeOrThrow(VariableEnum.class, String.valueOf(var.getVarType()));
        if (VariableEnum.DERIVE_VAR.equals(variableEnum)) {
            return depVarList;
        } else if (VariableEnum.BASIC_VAR.equals(variableEnum) ||
                VariableEnum.CONSTANT_VAR.equals(variableEnum)) {
            Optional<List<VarInterfaceInfo>> interfaceList = Optional
                    .ofNullable(varInterfaceInfoService.getInterfaceList(organId));
            if (interfaceList.isPresent()) {
                interfaceDepParamCodes = interfaceList.get().stream()
                        .map(item -> {
                            InterfaceVarDepVO interfaceVarDepVO = new InterfaceVarDepVO();
                            interfaceVarDepVO.setInputParam(item.getInputParam());
                            interfaceVarDepVO.setInterfaceId(Long.valueOf(item.getId()));
                            return interfaceVarDepVO;
                        })
                        .peek(varDepVO -> {
                            String[] inputVarCodes = StringUtils.split(varDepVO.getInputParam(), ",");
                            if (null != inputVarCodes && inputVarCodes.length > 0) {
                                varDepVO.setInputVarCodes(Arrays.asList(inputVarCodes));
                            }
                        })
                        .filter(varDepVO -> Optional.of(varDepVO).map(InterfaceVarDepVO::getInputVarCodes)
                                .map(CollectionUtils::isNotEmpty).orElse(false))
                        .filter(varDepVO -> varDepVO.getInputVarCodes().contains(varCode))
                        .map(item -> findVarCodesByInterfaceId(item.getInterfaceId(), organId))
                        .collect(Collectors.toList())
                        .stream()
                        .flatMap(Collection::stream)
                        .collect(Collectors.toList());
            }
        }

        varParamCodes = variableParamService.getDepVarParamCode(varCode, organId);
        varParamCodes.addAll(new HashSet<>(interfaceDepParamCodes));
        if (CollectionUtils.isNotEmpty(varParamCodes)) {
            depVarList = variableMapper.findByVarCodesAndOrganId(varParamCodes, organId)
                    .parallelStream()
                    .filter(Objects::nonNull)
                    .map(item -> {
                        VariableDepVO variableDepVO = new VariableDepVO();
                        variableDepVO.setVarCode(item.getVarCode());
                        variableDepVO.setDefaultValue(item.getVarDefaultValue());
                        variableDepVO.setVarName(item.getVarName());
                        variableDepVO.setVarType(String.valueOf(item.getVarType()));
                        variableDepVO.setValueType(String.valueOf(item.getVarValueType()));
                        return variableDepVO;
                    }).collect(Collectors.toList());
        }
        return depVarList;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public VariableResumeVO resumeVarSnapShot(String varCode, Long versionId, Long organId) {

        VariableSnapshot varSnapshot = new LambdaQueryChainWrapper<>(variableSnapshotMapper)
                .eq(VariableSnapshot::getVarCode, varCode)
                .eq(VariableSnapshot::getVersion, versionId)
                .eq(VariableSnapshot::getOrganId, organId)
                .eq(VariableSnapshot::getIsDeleted, 'N')
                .one();
        VariableResumeVO variableResumeVO = JSONObject.toJavaObject(JSON.parseObject(varSnapshot.getSnapshot()), VariableResumeVO.class);
        VariableSaveAndUpdateParam saveAndUpdateParam = DataConvertUtil.fromVariableResumeVO(variableResumeVO, organId);
        saveAndUpdateParam.setIsResume(Boolean.TRUE);
        try {
            if (checkVarNameIsDuplicateOnUpdate(varCode, variableResumeVO.getVarName(), organId)) {
                throw new ApiException(
                        ErrorCodeEnum.VAR_NAME_REPEAT.getCode(),
                        ErrorCodeEnum.VAR_NAME_REPEAT.getMessage());
            }
            updateVar(saveAndUpdateParam);
        } catch (Exception ex) {
            throw new ApiException(
                    ErrorCodeEnum.VAR_RESUME_SNAPSHOT_ERROR.getCode(),
                    ErrorCodeEnum.VAR_RESUME_SNAPSHOT_ERROR.getMessage(),
                    ex
            );
        }
        return variableResumeVO;
    }


    @Override
    public ResponseEntityDto<VarPageResult<List<VariableRespVO>, Variable>> findByVarType(Map<String, Object> paramMap) {
        if (!paramMap.containsKey("varType")) {
            throw new ApiException(ErrorCodeEnum.VAR_TYPE_EMPTY_ERROR.getCode(), ErrorCodeEnum.VAR_TYPE_EMPTY_ERROR.getMessage());
        }

        int pageNo = paramMap.get("pageNo") == null ? 1 : Integer.parseInt(paramMap.get("pageNo").toString());
        int pageSize = paramMap.get("pageSize") == null ? 10 : Integer.parseInt(paramMap.get("pageSize").toString());
        PageHelper.startPage(pageNo, pageSize);

        List<Variable> variableList = variableMapper.findByVarType(paramMap);
        if (CollectionUtils.isEmpty(variableList)) {
            return VarPageResult.success(new ArrayList<>(), new PageInfo<>());
        }
        PageInfo<Variable> pageInfo = new PageInfo<>(variableList);
        List<VariableRespVO> varResult = buildListVarVos((Integer) paramMap.get("varType"), variableList);
        return VarPageResult.success(varResult, pageInfo);
    }

    @Override
    public ResponseEntityDto<VarPageResult<List<VariableRespVO>, Variable>> findInterfaceVars(Map<String, Object> paramMap) {
        if (!paramMap.containsKey("varType")) {
            throw new ApiException(ErrorCodeEnum.VAR_TYPE_EMPTY_ERROR.getCode(), ErrorCodeEnum.VAR_TYPE_EMPTY_ERROR.getMessage());
        }
        int pageNo = paramMap.get("pageNo") == null ? 1 : Integer.parseInt(paramMap.get("pageNo").toString());
        int pageSize = paramMap.get("pageSize") == null ? 10 : Integer.parseInt(paramMap.get("pageSize").toString());
        PageHelper.startPage(pageNo, pageSize);
        List<Variable> variableList = variableMapper.findInterfaceVars(paramMap);
        if (CollectionUtils.isEmpty(variableList)) {
            return VarPageResult.success(new ArrayList<>(), new PageInfo<>());
        }
        PageInfo<Variable> pageInfo = new PageInfo<>(variableList);
        List<VariableRespVO> varReuslt = buildListVarVos((Integer) paramMap.get("varType"), variableList);
        return VarPageResult.success(varReuslt, pageInfo);
    }

    @Override
    public List<VariableDetailRespVO> findVarDetails(VarDetailReqVO varDetailReqVO) {
        List<Variable> varDetails = variableMapper.findVarDetails(varDetailReqVO.getVarCodes(), varDetailReqVO.getOrganId());
        List<VariableDetailRespVO> variableRespVOS = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(varDetails)) {
            variableRespVOS = varDetails.stream()
                    .map(var -> {
                        VariableDetailRespVO variableRespVO = new VariableDetailRespVO();
                        variableRespVO.setVarCode(var.getVarCode());
                        variableRespVO.setVarName(var.getVarName());
                        variableRespVO.setVarType(String.valueOf(var.getVarType()));
                        variableRespVO.setVarDefaultValue(var.getVarDefaultValue());
                        variableRespVO.setVarValueType(String.valueOf(var.getVarValueType()));
                        variableRespVO.setDesc(var.getVarDesc());
                        return variableRespVO;
                    }).collect(Collectors.toList());
        }
        return variableRespVOS;
    }

    @Override
    public List<String> findVarParams(VarParamReqVO varParamReqVO) {
        List<VariableParam> variableParams = new LambdaQueryChainWrapper<>(variableParamMapper)
                .select(VariableParam::getParamVariableCode)
                .eq(VariableParam::getVarCode, varParamReqVO.getVarCode())
                .eq(VariableParam::getOrganId, varParamReqVO.getOrganId())
                .eq(VariableParam::getIsDeleted, 'N')
                .list();
        return Optional.ofNullable(variableParams)
                .orElse(Collections.emptyList())
                .stream()
                .map(VariableParam::getParamVariableCode)
                .collect(Collectors.toList());
    }

    @Override
    public boolean checkSuperAdminUser(VarSuperAdminUserReqVO superAdminUserReqVO) {
        OrganUser adminUser = new LambdaQueryChainWrapper<>(organUserMapper)
                .select(OrganUser::getOrganId, OrganUser::getUserId)
                .eq(Objects.nonNull(superAdminUserReqVO.getOrganId()),
                        OrganUser::getOrganId, superAdminUserReqVO.getOrganId().intValue())
                .eq(Objects.nonNull(superAdminUserReqVO.getUserId()),
                        OrganUser::getUserId, superAdminUserReqVO.getUserId().intValue())
                .one();
        return Objects.nonNull(adminUser);
    }

    @Override
    public Map<String, Object> getEnumList(String type) {
        Class<? extends ICodeEnum> clazz;
        Map<String, Object> enumMap;
        String className = type;
        try {
            clazz = (Class<? extends ICodeEnum>) Class.forName(VariableConst.ENUM_PACKAGE + className);
            enumMap = EnumUtil.enumToListMap(clazz);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new ApiException(ErrorCodeEnum.VAR_ENUM_LIST_ERROR.getCode(), ErrorCodeEnum.VAR_ENUM_LIST_ERROR.getMessage(), e);
        }
        return enumMap;
    }

    private List<VariableRespVO> buildListVarVos(Integer varType, List<Variable> variableList) {
        VariableEnum variableEnum = EnumUtil.fromCode(VariableEnum.class, String.valueOf(varType));
        List<VariableRespVO> varReuslt = new ArrayList<>();

        List<String> varCodes = variableList.stream().map(Variable::getVarCode).filter(Objects::nonNull)
                .collect(Collectors.toList());
        Map<String, Map<String, String>> varCodeAndScriptMap = Maps.newHashMap();
        if (VariableEnum.DERIVE_VAR.equals(variableEnum)) {
            varCodeAndScriptMap = Optional.ofNullable(variableScriptService.findVarCodeAndScriptByVarCodes(varCodes))
                    .orElse(Collections.emptyMap());
        }

        for (Variable variable : variableList) {
            VariableRespVO variableVo = new VariableRespVO();
            variableVo.setVarCode(variable.getVarCode());
            variableVo.setVarName(variable.getVarName());
            variableVo.setVarDefaultValue(variable.getVarDefaultValue());
            variableVo.setVarValueType(String.valueOf(variable.getVarValueType()));
            variableVo.setModifier(variable.getModifier());
            variableVo.setCreator(variable.getCreator());
            variableVo.setDesc(variable.getVarDesc());
            variableVo.setVarType(String.valueOf(variable.getVarType()));
            variableVo.setConstValue(variable.getConstValue());
            variableVo.setInterfaceId(variable.getInterfaceId());
            variableVo.setInputParam(variable.getInputParam());
            variableVo.setSourceType(variable.getSourceType());
            variableVo.setInterfaceParseVar(variable.getInterfaceParseVar());
            variableVo.setIsInner(variable.getIsInner());
            variableVo.setIsOutput(variable.getIsOutput());
            variableVo.setIsInput(variable.getIsInput());
            variableVo.setProvider(variable.getProvider());
            variableVo.setApi(variable.getApi());
            variableVo.setCreateTime(variable.getCreateTime());
            variableVo.setUpdateTime(variable.getUpdateTime());
            if (VariableEnum.DERIVE_VAR.equals(variableEnum)) {
                variableVo.setScript(varCodeAndScriptMap.getOrDefault(variable.getVarCode(), Maps.newHashMap()).get(VariableConst.SCRIPT_CONTENT));
                variableVo.setParameterVariables(new HashSet<>(variableParamService.getVarParamCode(variable.getVarCode())));
            }
            varReuslt.add(variableVo);
        }
        return varReuslt;
    }


    @Override
    public void sqlVariableCheck(Map map) {
        if (map.containsKey("sqlStatement")) {
            Object sqlStatement = map.get("sqlStatement");
            if (sqlStatement != null && !"".equals(sqlStatement)) {
                String param = sqlStatement.toString().toUpperCase();
                for (String match : VariableConst.KEY_WORDS) {
                    if (param.contains(match)) {
                        throw new ApiException(ErrorCodeEnum.SQL_VARIABLE_HAVE_RISK.getCode(), ErrorCodeEnum.SQL_VARIABLE_HAVE_RISK.getMessage() + ":" + match);
                    }
                }
            }
        }
    }

    @Override
    public int isExists(Map<String, Object> paramMap) {
        return variableMapper.isExists(paramMap);
    }

    @Override
    public Map<String, Object> importExcel(String url, Map<String, Object> paramMap) {
        Map<String, Object> resultMap = new HashMap<>();

        InputStream is = null;
        Workbook Workbook = null;
        Sheet Sheet;
        try {
            is = new FileInputStream(url);
            Workbook = WorkbookFactory.create(is);
        } catch (IOException | InvalidFormatException e) {
            e.printStackTrace();
        }

        List<Variable> variableVoList = new ArrayList<Variable>();
        List<String> varCodeList = new ArrayList<>();
        int sucRows = 0; // 导入成功行数
        int failRows = 0; // 导入失败行数
        int repeatRows = 0; // 重复行数
        int existRows = 0; // 已存在的字段

        // 循环工作表 Sheet
        for (int numSheet = 0; numSheet < Workbook.getNumberOfSheets(); numSheet++) {
            Sheet = Workbook.getSheetAt(numSheet);
            if (Sheet == null) {
                continue;
            }
            // 循环行 Row
            for (int rowNum = 1; rowNum <= Sheet.getLastRowNum(); rowNum++) {
                try {
                    Row Row = Sheet.getRow(rowNum);
                    if (Row == null) {
                        continue;
                    }
                    Variable varVo = new Variable();
//                    varVo.setAuthor(Long.valueOf(paramMap.get("author").toString()));
                    varVo.setIsCommon(Integer.valueOf(paramMap.get("isCommon").toString()));

                    // 循环单元格 Cell
                    for (int cellNum = 0; cellNum <= Row.getLastCellNum(); cellNum++) {
                        Cell cell = Row.getCell(cellNum);
                        String cellStr = ExcelUtil.getCellValue(cell).trim();
                        switch (cellNum) { // 逐单元格处理
                            case 0:
                                varVo.setVarCode(cellStr);
                                break;
                            case 1:
                                varVo.setVarName(cellStr);
                                break;
                            case 2:
                                paramMap.put("varType", cellStr);
                                Long varTypeId = variableTypeMapper.findIdByVariableType(paramMap);
//                                if (varTypeId != 0) {
//                                    varVo.setVarTypeId(varTypeId);
//                                } else {
//                                    varVo.setVarTypeId(new Long(0)); //异常1：如果字段类型没法匹配，如何处理？
//                                }
                                break;
                            case 3:
                                Integer valueType = 0;
                                if (cellStr.equals("数值型")) {
                                    valueType = 1;
                                }
                                if (cellStr.equals("字符型")) {
                                    valueType = 2;
                                }
                                if (cellStr.equals("布尔型")) {
                                    valueType = 3;
                                }
                                if (cellStr.equals("浮点型")) {
                                    valueType = 4;
                                }
                                varVo.setVarValueType(valueType);
                                break;
//                            case 4:
//                                varVo.setValueScope(cellStr);
//                                break;
//                            case 5:
//                                if (ExcelUtil.getCellValue(cell).equals("Y")) {
//                                    varVo.setIsDerivative(1);
//                                } else {
//                                    varVo.setIsDerivative(0);
//                                }
//                                break;
                            case 4:
                                if (cellStr.equals("Y")) {
                                    varVo.setIsOutput(1);
                                } else if (cellStr.equals("N")) {
                                    varVo.setIsOutput(0);
                                }
                                break;
//                            case 7://这里只处理公式原始值，不确定回显字段及字段绑定
//                                varVo.setFormula(cellStr);
//                                break;
                            default:
                                break;
                        }
                    }
                    if (varVo.getVarCode() != null) {
                        paramMap.put("varCode", varVo.getVarCode());
                        Variable OldFieldVo = variableMapper.findByVarCodeOrVarName(paramMap);
                        if (OldFieldVo != null) {
                            existRows++;
                            // fieldVo.setUserId(OldFieldVo.getUserId());
                            // 不能直接更新字段，需先修改已使用到的地方
                            // fieldMapper.updateField(paramMap);
                        } else {
                            // 防止重复字段
                            if (varCodeList.contains(varVo.getVarCode())) {
                                repeatRows++;
                            } else {
                                sucRows++;
                                // 加入到list，等待批量更新
                                variableVoList.add(varVo);
                                varCodeList.add(varVo.getVarName());
                            }
                        }
                    }
                } catch (Exception e) {
                    failRows++;
                    e.printStackTrace();
                }
            }// end for Row
        }// end first sheet
        if (variableVoList.size() > 0) {
            variableMapper.batchAddVariable(variableVoList);
            paramMap.put("status", 1);// 导入后字段状态默认启用
            variableUserMapper.batchCreateVariableUserRel(paramMap);
        }
        resultMap.put("sucRows", sucRows);
        resultMap.put("failRows", failRows);
        resultMap.put("repeatRows", repeatRows);
        resultMap.put("existRows", existRows);
        return resultMap;
    }


    @Override
    public List<Variable> getVariableList(Map<String, Object> paramMap) {
        return variableMapper.getVariableList(paramMap);
    }


    @Override
    public int updateVariableFolder(UpdateFolderParam param) {
        return variableMapper.updateVariableFolder(param);
    }


    @Override
    public Variable findByVariableCode(Map<String, Object> paramMap) {
        return variableMapper.findByVariableCode(paramMap);
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public int updateVar(VariableSaveAndUpdateParam variable) {
        int updated;
        VariableEnum variableEnum = EnumUtil.fromCodeOrThrow(VariableEnum.class, String.valueOf(variable.getVarType()));
        String varCode = variable.getVarCode();
        LambdaUpdateWrapper<VariableUser> lbd = new LambdaUpdateWrapper<>();
        lbd.eq(VariableUser::getVarCode, variable.getVarCode())
                .set(VariableUser::getUpdated, DateUtils.getCurrentDate());
        try {
            updated = variableMapper.updateVariable(variable);
            variableInterfaceService.update(buildInterfaceVO(variable));
            variableUserMapper.update(null, lbd);
            if (VariableEnum.DERIVE_VAR.equals(variableEnum)) {
                variableParamMapper.deleteVariableParam(varCode, variable.getOrganId());
                List<VariableParamVO> variableParams = new ArrayList<>();
                if (CollectionUtils.isNotEmpty(variable.getParameterVariables())) {
                    variable.getParameterVariables().forEach(inputVariable -> {
                        variableParams.add(new VariableParamVO(varCode, inputVariable, variable.getOrganId()));
                    });
                }
                if (!variable.getIsResume()) {
                    Long latestVersion = variableSnapshotMapper.findLatestVersion(variable.getVarCode(), variable.getOrganId());
                    VariableSnapshot variableSnapshot = buildVarSnapshot(variable, latestVersion);
                    variableSnapshotMapper.insert(variableSnapshot);
                }
                try {
                    if (CollectionUtils.isNotEmpty(variableParams)) {
                        variableParamMapper.addVariableParams(variableParams);
                    }
                    variableScriptMapper.updateVariableScript(varCode, variable.getOrganId(), variable.getModifier(), variable.getScript());
                } catch (Exception exception) {
                    throw new ApiException(
                            ErrorCodeEnum.VAR_PARAM_ERROR.getCode(),
                            ErrorCodeEnum.VAR_PARAM_ERROR.getMessage(),
                            exception
                    );
                }

            }
        } catch (Exception exception) {
            throw new ApiException(
                    ErrorCodeEnum.VAR_UPDATE_ERROR.getCode(),
                    ErrorCodeEnum.VAR_UPDATE_ERROR.getMessage(),
                    exception
            );
        }
        return updated;
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteVar(VariableDeleteParam variable) {
        try {
            variableMapper.deleteVariable(variable);
            VariableEnum variableEnum = EnumUtil.fromCodeOrThrow(VariableEnum.class, String.valueOf(variable.getVarType()));
            if (VariableEnum.DERIVE_VAR.equals(variableEnum)) {
                variableScriptMapper.deleteVarScript(variable);
                variableParamMapper.logicalDeleteVarParam(variable);
                variableSnapshotMapper.logicalDelete(variable.getVarCode(), variable.getUserId());
            }

        } catch (Exception exception) {
            throw new ApiException(
                    ErrorCodeEnum.VAR_DELETE_ERROR.getCode(),
                    ErrorCodeEnum.VAR_DELETE_ERROR.getMessage(),
                    exception
            );
        }
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void batchDeleteVar(VariableBatchDeleteParam batchDeleteParam) {
        //TODO: checkVariable（checkUsing,checkParam)
        try {
            variableMapper.batchDeleteVariable(batchDeleteParam);
            VariableEnum variableEnum = EnumUtil.fromCodeOrThrow(VariableEnum.class, String.valueOf(batchDeleteParam.getVarType()));
            if (VariableEnum.DERIVE_VAR.equals(variableEnum)) {
                variableScriptMapper.batchDeleteVarScript(batchDeleteParam);
                variableParamMapper.batchLogicalDeleteVarParam(batchDeleteParam);

                VarSnapshotBatchDeleteParam snapBatchDeleteParam = new VarSnapshotBatchDeleteParam();
                snapBatchDeleteParam.setUserId(batchDeleteParam.getUserId());
                snapBatchDeleteParam.setVarCodes(batchDeleteParam.getVarCodes());
                variableSnapshotMapper.batchLogicalDelete(snapBatchDeleteParam);
            }

        } catch (Exception exception) {
            throw new ApiException(
                    ErrorCodeEnum.VAR_DELETE_ERROR.getCode(),
                    ErrorCodeEnum.VAR_DELETE_ERROR.getMessage(),
                    exception
            );
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public int addVar(VariableSaveAndUpdateParam variable) {
        int inserted;
        VariableEnum variableEnum = EnumUtil.fromCodeOrThrow(VariableEnum.class, String.valueOf(variable.getVarType()));
        VariableUser variableUser = new VariableUser();
        variableUser.setCreated(DateUtils.getCurrentDate());
        variableUser.setUpdated(DateUtils.getCurrentDate());
        variableUser.setOrganId(Optional.ofNullable(variable.getOrganId()).map(Long::intValue).orElse(1));
        variableUser.setVarCode(variable.getVarCode());
        variableUser.setUserId(Optional.ofNullable(variable.getUserId()).map(Long::intValue).orElse(1));
        variableUser.setStatus(1);
        try {
            inserted = variableMapper.addVariable(variable);
//            variableUserMapper.insert(variableUser);
            if (VariableEnum.DERIVE_VAR.equals(variableEnum)) {
                VariableScript variableScript = buildVariableScript(variable);
                variableScriptMapper.insert(variableScript);
                List<VariableParamVO> variableParams = new ArrayList<>();
                if (CollectionUtils.isNotEmpty(variable.getParameterVariables())) {
                    variable.getParameterVariables().forEach(inputVariable -> {
                        variableParams.add(new VariableParamVO(variable.getVarCode(), inputVariable, variable.getOrganId()));
                    });
                }
                Long latestVersion;
                if (variable.getIsCopyVar()) {
                    latestVersion = variableSnapshotMapper.findLatestVersion(variable.getCopyVarOriginCode(), variable.getOrganId());
                } else {
                    latestVersion = variableSnapshotMapper.findLatestVersion(variable.getVarCode(), variable.getOrganId());
                }
                VariableSnapshot variableSnapshot = buildVarSnapshot(variable, Optional.ofNullable(latestVersion).orElse(0L));
                variableSnapshotMapper.insert(variableSnapshot);
                try {
                    if (CollectionUtils.isNotEmpty(variableParams)) {
                        variableParamMapper.addVariableParams(variableParams);
                    }
                } catch (Exception exception) {
                    throw new ApiException(
                            ErrorCodeEnum.VAR_PARAM_ERROR.getCode(),
                            ErrorCodeEnum.VAR_PARAM_ERROR.getMessage(),
                            exception
                    );
                }
            } else if (VariableEnum.INTERFACE_VAR.equals(variableEnum)) {
                VariableInterfaceVO variableInterfaceVO = new VariableInterfaceVO();
                variableInterfaceVO.setVariableCode(variable.getVarCode());
                variableInterfaceVO.setApi(variable.getApi());
                variableInterfaceVO.setProvider(variable.getProvider());
                variableInterfaceVO.setDatasourceId(variable.getInterfaceId().intValue());
                variableInterfaceVO.setDatasourceType(0);
                variableInterfaceVO.setOrganId(variable.getOrganId());
                try {
                    variableInterfaceService.save(variableInterfaceVO);
                } catch (Exception e) {
                    throw new ApiException(
                            ErrorCodeEnum.VAR_INTERFACE_PRIVIDER_API_ERROR.getCode(),
                            ErrorCodeEnum.VAR_INTERFACE_PRIVIDER_API_ERROR.getMessage(),
                            e
                    );
                }
            }

        } catch (DuplicateKeyException exception) {
            throw new ApiException(
                    ErrorCodeEnum.VAR_EXIST_ERROR.getCode(),
                    ErrorCodeEnum.VAR_EXIST_ERROR.getMessage(),
                    exception
            );
        } catch (Exception exception) {
            throw new ApiException(
                    ErrorCodeEnum.VAR_ADD_ERROR.getCode(),
                    ErrorCodeEnum.VAR_ADD_ERROR.getMessage(),
                    exception
            );
        }
        return inserted;
    }

    @Override
    public boolean checkVarCodeNameIsLegal(String varCode) {
        return !(StringUtils.isBlank(varCode) ||
                varCode.matches("[0-9]+") ||
                VariableNumericUtil.isStartWithNumber(varCode));

    }

    @Override
    public void copyVar(VariableCopyToFolderReqVo copyToFolderReqVo) {

        List<Variable> variables = findVariablesByOrganId(Collections.singletonList(copyToFolderReqVo.getVarCode()), copyToFolderReqVo.getOrganId());
        if (CollectionUtils.isEmpty(variables)) {
            return;
        }
        List<Future<Void>> allFutureList = Lists.newArrayList();
        variables.forEach(var -> allFutureList.add(CompletableFuture.runAsync(() -> {
            VariableSaveAndUpdateParam variableVo = BeanUtilsPlus.copyProperties(var, VariableSaveAndUpdateParam::new);
            VariableEnum variableEnum = EnumUtil.fromCodeOrThrow(VariableEnum.class, String.valueOf(copyToFolderReqVo.getVarType()));
            if (VariableEnum.DERIVE_VAR.equals(variableEnum)) {
                Set<String> varParamCode = variableParamMapper.getVarParamCode(variableVo.getVarCode());
                String script = variableScriptMapper.findScriptByVariableCode(variableVo.getVarCode());
                variableVo.setParameterVariables(new ArrayList<>(varParamCode));
                variableVo.setScript(script);
            }

            variableVo.setValueType(var.getVarValueType());
            variableVo.setUserId(copyToFolderReqVo.getUserId());
            variableVo.setCreator(copyToFolderReqVo.getModifier());
            variableVo.setModifier(copyToFolderReqVo.getModifier());
            variableVo.setVarName(copyToFolderReqVo.getVarCopyName());
            variableVo.setFolderId(Optional.ofNullable(copyToFolderReqVo.getFolderId()).map(Long::intValue).orElse(0));
            variableVo.setDesc(copyToFolderReqVo.getDesc());
            variableVo.setVarCode(copyToFolderReqVo.getVarCopyCode());
            variableVo.setVarType(copyToFolderReqVo.getVarType());
            variableVo.setOrganId(copyToFolderReqVo.getOrganId());
            variableVo.setIsCopyVar(Boolean.TRUE);
            variableVo.setCopyVarOriginCode(copyToFolderReqVo.getVarCopyCode());

            List<VariableSnapshot> variableSnapshots = buildVarSnapshotList(
                    copyToFolderReqVo.getVarCode(),
                    copyToFolderReqVo.getVarCopyCode(),
                    copyToFolderReqVo.getOrganId(),
                    copyToFolderReqVo.getModifier());

            transactionTemplate.execute(status -> {
                try {
                    if (CollectionUtils.isNotEmpty(variableSnapshots)) {
                        variableSnapshotService.saveBatch(variableSnapshots);
                    }
                    addVar(variableVo);
                    return Boolean.TRUE;
                } catch (DuplicateKeyException exception) {
                    throw new ApiException(
                            ErrorCodeEnum.VAR_EXIST_ERROR.getCode(),
                            ErrorCodeEnum.VAR_EXIST_ERROR.getMessage(),
                            exception
                    );
                } catch (Exception e) {
                    status.setRollbackOnly();
                    log.error("复制变量失败，变量类型:{}, 变量code:{}",
                            variableEnum.getDesc(),
                            variableVo.getVarCode(),
                            e);
                    throw new ApiException(ErrorCodeEnum.VAR_COPY_ERROR.getCode(),
                            ErrorCodeEnum.VAR_COPY_ERROR.getMessage());
                }
            });
        }, bizProcessorThreadPool)));
        allFutureList.forEach(var -> FutureUtils.futureGet(var, ErrorCodeEnum.VAR_COPY_ERROR.getCode(),
                ErrorCodeEnum.VAR_COPY_ERROR.getMessage()));

    }


    private List<VariableSnapshot> buildVarSnapshotList(String varCode, String copyVarCode, Long organId, String modifier) {
        List<VariableSnapshot> snapshotList = variableSnapshotMapper.findByVarCode(varCode, organId);
        return Optional.ofNullable(snapshotList)
                .orElse(Collections.emptyList())
                .stream()
                .map(snapshot ->
                        DataConvertUtil.fromVarSnapshot(snapshot, copyVarCode, snapshot.getSnapshot())
                )
                .collect(Collectors.toList());
    }

    private VariableScript buildVariableScript(VariableSaveAndUpdateParam variable) {
        VariableScript variableScript = new VariableScript();
        variableScript.setCreator(variable.getCreator());
        variableScript.setScriptContent(variable.getScript());
        variableScript.setVarCode(variable.getVarCode());
        variableScript.setScriptType(2);
        variableScript.setStatus(1);
        variableScript.setOrganId(variable.getOrganId());
        return variableScript;
    }

    private VariableSnapshot buildVarSnapshot(VariableSaveAndUpdateParam variable, Long currentVersion) {
        return DataConvertUtil.fromVariableSaveAndUpdateParam(variable, currentVersion);
    }

    private VariableInterfaceVO buildInterfaceVO(VariableSaveAndUpdateParam variable) {
        VariableInterfaceVO variableInterfaceVO = new VariableInterfaceVO();
        variableInterfaceVO.setDatasourceType(0);
        variableInterfaceVO.setApi(variable.getApi());
        variableInterfaceVO.setVariableCode(variable.getVarCode());
        variableInterfaceVO.setProvider(variable.getProvider());
        variableInterfaceVO.setOrganId(variable.getOrganId());
        return variableInterfaceVO;
    }

    private VariableSnapshot buildCopyVarSnapshot(String copyVarCode, Long currentVersion, String modifier, String snapshot) {
        VariableSnapshot variableSnapshot = new VariableSnapshot();
        variableSnapshot.setVarCode(copyVarCode);
        variableSnapshot.setVersion(currentVersion + 1);
        variableSnapshot.setSnapshot(snapshot);
        variableSnapshot.setCreator(modifier);
        variableSnapshot.setModifier(modifier);
        variableSnapshot.setCreateTime(DateUtils.getCurrentDate());
        variableSnapshot.setUpdateTime(DateUtils.getCurrentDate());
        return variableSnapshot;
    }


    @Override
    public boolean updateVariabletDefaultValue(Variable variable) {

        boolean isValid = true;
        if (StringUtils.isEmpty(variable.getVarDefaultValue())) {
            variable.setVarDefaultValue("");
        }
        VariableEnum variableEnum = EnumUtil.fromCode(VariableEnum.class, String.valueOf(variable.getVarValueType()));
        switch (variableEnum.name().toLowerCase()) {
            case "integer":
                if (!isInteger(variable.getVarDefaultValue())) {
                    isValid = false;
                }
                break;
            case "double":
                if (!isDouble(variable.getVarDefaultValue())) {
                    isValid = false;
                }
                break;
            case "boolean":
                if (!"true".equals(variable.getVarDefaultValue()) && !"false".equals(variable.getVarDefaultValue())) {
                    isValid = false;
                }
                break;
            default:
                isValid = true;
        }
        if (isValid) {
            variableDetailMapper.updateVariableDefaultValue(variable.getVarCode(), variable.getVarDefaultValue());
        }
        return isValid;
    }

    @Override
    public List<Variable> findByUser(Map<String, Object> paramMap) {
        return variableMapper.findByUser(paramMap);
    }

    @Override
    public boolean updateVarType(VariableTreeParam param) {
        param.setOrganId(SessionManager.getLoginAccount().getOrganId());
        param.setUserId(SessionManager.getLoginAccount().getId());
        variableTypeMapper.updateVariableType(param);
        variableTypeUserMapper.updateVariableTypeUserRel(param);
        return true;
    }


    @Override
    public void batchInsertOrUpdateVariables(List<Variable> variables) {
        variableMapper.batchInsertOrUpdateVariables(variables);
    }


    private boolean isDouble(String str) {
        try {
            Double.parseDouble(str);
            return true;
        } catch (NumberFormatException ex) {
        }
        return false;
    }

    private boolean isInteger(String str) {
        try {
            Integer.parseInt(str);
            return true;
        } catch (NumberFormatException ex) {
        }
        return false;
    }


    /**
     * 查找继承自某变量的所有变量Code拼成逗号分隔的字符串返回
     *
     * @return
     */
    public String getVariable(String varCodes, String usedVarCode, String engineId) {

        Map<String, Object> param = new HashMap<String, Object>();
        param.put("usedVarCode", usedVarCode);
//        param.put("engineId", engineId);

        Long userId = SessionManager.getLoginAccount().getId();
        Long organId = SessionManager.getLoginAccount().getOrganId();

        param.put("userId", userId);
        param.put("organId", organId);

        varCodes = "";

        String str = variableMapper.checkVariable(param);

        if (str != null && str.length() >= 0) {

            String arrIds[] = str.split(",");
            for (int i = 0; i < arrIds.length; i++) {
                if (varCodes.equals("")) {
                    varCodes = getVariable("", arrIds[i], engineId);
                } else {
                    varCodes = varCodes + "," + getVariable("", arrIds[i], engineId);
                }

            }
        } else {
            return usedVarCode;
        }
        return varCodes;
    }

}
